﻿using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Globalization;
using System.Linq;
using System.Windows;
using System.Windows.Data;
using System.Windows.Input;
using JelleDruyts.Windows;
using JelleDruyts.Windows.Collections;
using JelleDruyts.Windows.ObjectModel;
using Schedulr.Infrastructure;
using Schedulr.Messages;
using Schedulr.Models;

namespace Schedulr.ViewModels
{
    public abstract class PictureListViewModel : ViewModel
    {
        #region Fields

        private ObservableCollectionSynchronizer<Picture, PictureViewModel> synchronizer;

        #endregion

        #region Properties

        protected RelayCommand RemoveCommand { get; private set; }
        protected Account Account { get; private set; }
        protected ObservableCollection<Picture> PictureList { get; private set; }
        protected ObservableCollection<PictureViewModel> PictureListViewModels { get; private set; }
        protected IList<Picture> SelectedPictures { get; private set; }
        public PictureQueueViewModel PictureQueue { get; private set; }

        #endregion

        #region Observable Properties

        /// <summary>
        /// Gets or sets the picture details view model.
        /// </summary>
        public PictureDetailsViewModel PictureDetails
        {
            get { return this.GetValue(PictureDetailsProperty); }
            set { this.SetValue(PictureDetailsProperty, value); }
        }

        /// <summary>
        /// The definition for the <see cref="PictureDetails"/> observable property.
        /// </summary>
        public static ObservableProperty<PictureDetailsViewModel> PictureDetailsProperty = new ObservableProperty<PictureDetailsViewModel, PictureListViewModel>(o => o.PictureDetails);

        #endregion

        #region Constructors

        protected PictureListViewModel()
        {
            this.RemoveCommand = new RelayCommand(this.Remove, this.CanRemove, "_Remove", "Removes the currently selected pictures from the queue [DEL]", new KeyGesture(Key.Delete));
            this.PictureQueue = new PictureQueueViewModel(this.IsDragDropSupportedOnQueue, this.GetQueueCommands());
            this.SelectedPictures = new Picture[0];
            this.PictureList = new ObservableCollection<Picture>();
            Messenger.Register<AccountActionMessage>(OnAccountActionMessage);
            Messenger.Register<PictureQueueSelectionChangedMessage>(OnPictureQueueSelectionChanged);
            Messenger.Register<PictureQueueChangedMessage>(OnPictureQueueChanged);
        }

        #endregion

        #region Message Handlers

        private void OnAccountActionMessage(AccountActionMessage message)
        {
            if (message.Action == ListAction.CurrentChanged)
            {
                this.Account = message.Account;
                if (this.Account != null)
                {
                    this.PictureList = GetPicturesForAccount(this.Account);
                }
                else
                {
                    this.PictureList = new ObservableCollection<Picture>();
                }

                var groupedPicturesView = new CollectionViewSource();
                this.PictureListViewModels = new ObservableCollection<PictureViewModel>(this.PictureList.Select(p => new PictureViewModel(p, this.Account)));
                groupedPicturesView.Source = this.PictureListViewModels;
                if (this.Account != null)
                {
                    groupedPicturesView.GroupDescriptions.Add(new BatchIdGroupDescription(this.Account, this.PictureList));
                }
                this.PictureQueue.Pictures = groupedPicturesView.View;

                // Dispose the previous synchronizer if there was one (to avoid leaking event handlers).
                if (this.synchronizer != null)
                {
                    this.synchronizer.Dispose();
                }
                // Synchronize the picture view models with the source pictures.
                this.synchronizer = new ObservableCollectionSynchronizer<Picture, PictureViewModel>(this.PictureList, p => new PictureViewModel(p, this.Account), this.PictureListViewModels, p => p.Picture);
            }
        }

        private void OnPictureQueueSelectionChanged(PictureQueueSelectionChangedMessage message)
        {
            if (message.PictureQueue == this.PictureQueue)
            {
                this.SelectedPictures = message.SelectedPictures;
                var isReadOnly = this.ArePictureDetailsReadOnly;
                this.PictureDetails = new PictureDetailsViewModel(this.Account, this.SelectedPictures, Visibility.Visible, Visibility.Visible, isReadOnly);
            }
        }

        private void OnPictureQueueChanged(PictureQueueChangedMessage message)
        {
            if (message.Reason == PictureQueueChangedReason.BatchesNormalized)
            {
                if (this.PictureQueue != null && this.PictureQueue.Pictures != null)
                {
                    this.PictureQueue.Pictures.Refresh();
                }
            }
        }

        #endregion

        #region Commands

        private bool CanRemove(object parameter)
        {
            return (this.SelectedPictures.Count > 0);
        }

        private void Remove(object parameter)
        {
            var removingPicturesTask = new ApplicationTask("Removing pictures", this.SelectedPictures.Count);
            var step = 0;
            Messenger.Send<TaskStatusMessage>(new TaskStatusMessage(removingPicturesTask));
            try
            {
                var i = 0;
                foreach (var picture in this.SelectedPictures)
                {
                    removingPicturesTask.SetProgress(step, string.Format(CultureInfo.CurrentCulture, "Removing \"{0}\" ({1} of {2})", picture.Title, ++i, removingPicturesTask.TotalSteps));
                    step++;
                    this.PictureList.Remove(picture);
                }
                Tasks.NormalizeBatches(this.PictureList);
            }
            finally
            {
                removingPicturesTask.SetComplete(string.Format(CultureInfo.CurrentCulture, "{0} pictures removed", removingPicturesTask.TotalSteps));
            }
        }

        #endregion

        #region Abstract Members

        protected abstract ObservableCollection<Picture> GetPicturesForAccount(Account account);

        protected abstract bool IsDragDropSupportedOnQueue { get; }

        protected abstract IEnumerable<ICommand> GetQueueCommands();

        protected abstract bool ArePictureDetailsReadOnly { get; }

        #endregion
    }
}